<?php
/**
 * 提现相关
 * xionggan
 * Date: 2019/06/22
 */

namespace service;

use think\Db;
use think\Exception;
use other\BaseRedis;
use service\pay\VirtualPay;
use app\common\model\User;
use app\common\model\CoreTransfer;

class TransferService
{

    protected $type             = null;
    protected $prefix           = null;
    protected $way              = null;
    protected $way_prefix       = null;
    protected $transfer_sn      = null;
    protected $transfer_model   = null;

    public function __construct()
    {
        $this->transfer_model = new CoreTransfer();
    }

    /**
     * 设置提现类型
     * @param   string  $type   提现类型
     * @return  null
     */
    public function setType($type)
    {
        $map = $this->transfer_model->getTypeMap();

        foreach ($map as $key => $value) {
            if ($value['prefix'] == $type) {
                $this->type     = $key;
                $this->prefix   = $value['prefix'];
                break;
            }
        }

        if (empty($this->type)) {
            throw new Exception('提现类型错误');
        }
    }

    /**
     * 设置提现渠道
     * @param   string  $way   提现渠道
     * @return  null
     */
    public function setWay($way)
    {
        $map = $this->transfer_model->getWayMap();

        foreach ($map as $key => $value) {
            if ($value['prefix'] == $way) {
                $this->way          = $key;
                $this->way_prefix   = $value['prefix'];
                break;
            }
        }

        if (empty($this->way)) {
            throw new Exception('提现渠道错误');
        }
    }

    /**
     * 获取提现单号
     * @return  string  单号
     */
    public function getSn()
    {
        if (empty($this->type)) {
            throw new Exception('缺少提现类型');
        }

        $this->transfer_sn = $this->prefix.date('ymd').substr(
            implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8
        );

        try {
            $redis = new BaseRedis();

            if($redis->has("transfer_sn:".$this->transfer_sn)) {
                return $this->getSn();
            }

            $redis->set("transfer_sn:".$this->transfer_sn, '', 60);
        } catch (Exception $e) {}

        return $this->transfer_sn;
    }

    /**
     * 申请提现
     * @param   array   $params     提现数据
     * @return  string  单号
     */
    public function apply($params)
    {
        $format = [
            'store_id', 'user_id', 'open_id', 'amount', 
            'check_name', 'real_name', 'desc', 'ip'
        ];

        foreach ($format as $value) {
            if (!isset($params[$value])) {
                throw new Exception('申请提现,缺少参数:'.$value);
            }
        }

        if (empty($this->type)) {
            throw new Exception('缺少提现类型');
        }

        //默认提现方式
        if (empty($this->way)) {
            $this->setWay('LQ');
        }

        if (empty($this->transfer_sn)) {
            $this->getSn();
        }

        $user_model     = new User();
        $pay_service    = new VirtualPay();

        $user_data = $user_model->getOneByUserId($params['user_id']);

        if (empty($user_data)) {
            throw new Exception('提现账户不存在');
        }

        $amount_sum = $this->transfer_model->getSumByDay($params['user_id']);
        $amount_sum2 = bcadd($amount_sum, $params['amount']);

        if ($amount_sum2 > 500000) {
            $apply_sum = bcdiv(bcsub(500000, $amount_sum), 100, 2);
            throw new Exception('当日最多提现5000元，本日还可以申请'.$apply_sum.'元');
        }

        if ($params['amount'] > ($user_data['user_balance'] * 100)) {
            throw new Exception('提现金额超出的可提现金额');
        }

        $params['transfer_sn']  = $this->transfer_sn;
        $params['type']         = $this->type;
        $params['way']          = $this->way;
        $params['create_time']  = time();

        try {          
            $res = $pay_service->public_update_user_virtual(
                $params['user_id'], '', 2, 1, bcdiv($params['amount'], 100, 2), 0, 9, $this->transfer_sn, $params['desc']
            );

            if ($res <= 0) {
                throw new Exception('申请提现失败');
            }

            $this->transfer_model->addOne($params);
        } catch (Exception $e) {
            throw new Exception('申请提现失败');
        }

        $transfer_sn = $this->transfer_sn;
        $this->transfer_sn = null;
        return $transfer_sn;
    }

    /**
     * 提现
     * @param   array   $transfer_sn    提现单号
     * @param   boole   $retry          失败再次发起提现
     * @return  boole
     */
    public function transfer($transfer_sn, $retry=false)
    {
        $transfer   = $this->transfer_model->getOneBySn($transfer_sn);
        $way_map    = $this->transfer_model->getWayMap();
        $type_map   = $this->transfer_model->getTypeMap();

        if (empty($transfer)) {
            throw new Exception('提现申请不存在');
        }

        if ($transfer['status'] != CoreTransfer::STATUS_0 && !$retry) {
            throw new Exception('提现申请状态错误');
        }

        if ($transfer['status'] != CoreTransfer::STATUS_3 && $retry) {
            throw new Exception('重新提现申请状态错误');
        }

        if (empty($transfer['way']) || empty($way_map[$transfer['way']])) {
            throw new Exception('提现渠道错误');
        }

        if (empty($transfer['type']) || empty($type_map[$transfer['type']])) {
            throw new Exception('提现类型错误');
        }

        $result = $this->doTransfer($transfer);

        $this->transfer_model->updateStatusSuccessed(
            $result['transfer_sn'], $result['payment_no'], $result['payment_time']
        );

        $this->doListen($transfer, $result, 'transfer');

        return true;
    }

    /**
     * 拒绝申请
     * @param   array   $transfer_sn    提现单号
     * @return  boole
     */
    public function reject($transfer_sn)
    {
        $transfer = $this->transfer_model->getOneBySn($transfer_sn);

        if (empty($transfer)) {
            throw new Exception('提现申请不存在');
        }

        if (!in_array($transfer['status'], [CoreTransfer::STATUS_0, CoreTransfer::STATUS_3])) {
            throw new Exception('提现申请状态错误');
        }

        $pay_service = new VirtualPay();

        $res = $pay_service->public_update_user_virtual(
            $transfer['user_id'], '', 1, 1, bcdiv($transfer['amount'], 100, 2), 0, 9, $transfer['transfer_sn'], $transfer['desc'].'申请不通过'
        );

        if ($res <= 0) {
            throw new Exception('拒绝申请失败');
        }

        $this->transfer_model->updateStatusRejected($transfer['transfer_sn']);
        $this->doListen($transfer, [], 'reject');

        return true;
    }

    /**
     * 获取订单列表
     * @param   array   $params 查询参数: ['condition' => [], 'sort' => ['field' => '', 'dire' => ''], 'page' => 1, 'limit' => 10]
     * @return  array           订单数据
     */
    public function getList($params=[])
    {
        if (!empty($this->type)) {
            $params['condition']['type'] = $this->type;
        }

        return $this->transfer_model->getList($params);
    }

    /**
     * 获取订单状态表
     * @return  array 状态列表
     */
    public function getStatusMap()
    {
        return $this->transfer_model->getStatusMap();
    }

    /**
     * 获取订单类型表
     * @return  array 类型列表
     */
    public function getTypeMap()
    {
        return $this->transfer_model->getTypeMap();
    }

    /**
     * 运行提现
     * @param   array  $data    提现申请数据
     * @return  array           提现流水号等
     */
    protected function doTransfer($data)
    {
        $way_map    = $this->transfer_model->getWayMap();
        $type_map   = $this->transfer_model->getTypeMap();

        try {
            $way_prefix = $way_map[$data['way']]['prefix'];
            $way_name   = 'service\transfer\way\\transferWay'.strtoupper($way_prefix);

            if (!class_exists($way_name)) {
                throw new Exception('提现方式服务类不存在: '.$way_name);
            }

            $way_service = new $way_name($type_map[$data['type']]['prefix']);

            if (!method_exists($way_service, 'transferWay')) {
                throw new Exception('订单服务类方法不存在: transferWay');
            }

            $res = call_user_func_array([$way_service, 'transferWay'], [$data]);

            if (!$res) {
                $this->transfer_model->updateStatusFailed($data['transfer_sn'], '提现失败');
                throw new Exception('提现失败');
            }
        } catch (Exception $e) {
            $this->transfer_model->updateStatusFailed($data['transfer_sn'], $e->getMessage());
            throw new Exception($e->getMessage());
        }

        return $res;
    }

    /**
     * 运行提现成功后动作
     * @param   array   $data    提现申请数据
     * @param   array   $result  提现流水号等
     * @param   string  $action  动作：transfer, reject
     * @return  null
     */
    protected function doListen($data, $result, $action)
    {
        $type_map = $this->transfer_model->getTypeMap();

        try {
            $type_prefix = $type_map[$data['type']]['prefix'];
            $type_name   = 'service\transfer\type\\transferType'.strtoupper($type_prefix);

            if (!class_exists($type_name)) {
                return;
            }

            $type_service = new $type_name;

            if (!method_exists($type_service, 'run')) {
                return;
            }

            call_user_func_array([$type_service, 'run'], [$data, $result, $action]);
        } catch (Exception $e) {
            return;
        }
    }

}
